package com.zmn.oms.zmn.business.impl.grab;

import cn.hutool.core.util.BooleanUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.zmn.base.engineer.common.enums.EngineerStopOrderStatusEnum;
import com.zmn.base.oms.common.model.es.orderwork.EsOrderWork;
import com.zmn.biz.engineer.common.dro.area.EngineerServScopeStatusDRO;
import com.zmn.biz.engineer.common.dro.distribute.EngineerDistributeOrderDRO;
import com.zmn.biz.engineer.common.query.area.EngineerServScopeStatusQuery;
import com.zmn.biz.engineer.common.query.distribute.DistributeOrderSkillQuery;
import com.zmn.biz.engineer.common.query.distribute.EngineerEnforceDistributeOrderQuery;
import com.zmn.biz.engineer.dubbo.interfaces.area.EngineerServAreaListRemoteService;
import com.zmn.biz.engineer.dubbo.interfaces.distribute.PlatDistributeOrderListRemoteService;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.collection.CollectionUtil;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.map.Point;
import com.zmn.common.utils.math.MathUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.consts.GlobalConsts;
import com.zmn.gms.common.dio.grid.online.engineer.DistanceGridEngInfoQuery;
import com.zmn.gms.dubbo.interfaces.grid.online.engineer.GridEngineerListRemoteService;
import com.zmn.manager.mq.constant.MqTopicConsts;
import com.zmn.manager.mq.interfaces.ZmnMQSender;
import com.zmn.oms.business.interfaces.baidumap.BaiduMapBService;
import com.zmn.oms.business.interfaces.conf.distribute.NonGridRobOrderConfigBService;
import com.zmn.oms.business.interfaces.es.OrderWorkEsBService;
import com.zmn.oms.common.constant.*;
import com.zmn.oms.common.dro.conf.distribute.DistributeConfigDRO;
import com.zmn.oms.common.dro.conf.distribute.NonGridRobOrderConfigDRO;
import com.zmn.oms.manager.utils.OmsMqTagConsts;
import com.zmn.oms.model.dto.graborder.MqGrabOrderDTO;
import com.zmn.oms.model.dto.work.modify.must.OrderWorkGrabDTO;
import com.zmn.oms.model.entity.conf.distribute.NonGridRobOrderConfigQuery;
import com.zmn.oms.model.entity.detail.OrderDetail;
import com.zmn.oms.model.entity.grab.NonGridGrabOrder;
import com.zmn.oms.model.entity.work.DistributeMasterQuery;
import com.zmn.oms.model.entity.work.OrderWork;
import com.zmn.oms.model.vo.work.ZsOrderDistributeVO;
import com.zmn.oms.services.interfaces.detail.OrderDetailService;
import com.zmn.oms.services.interfaces.grab.NonGridGrabOrderService;
import com.zmn.oms.services.interfaces.work.OrderWorkService;
import com.zmn.oms.zmn.business.interfaces.grab.NonGridGrabDistributeBService;
import com.zmn.oms.zmn.business.interfaces.grab.NonGridGrabOrderBService;
import com.zmn.oms.zmn.business.interfaces.work.customwork.ZsDistributeWorkBService;
import com.zmn.performance.common.dio.MasterOverviewDIO;
import com.zmn.performance.common.dro.master.MasterOverviewIntegrateDRO;
import com.zmn.performance.dubbo.interfaces.master.MasterOverviewListRemoteService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.SourceFilter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 类描述: 无网格派单（抢单）实现类
 *
 * @author: HuangChao
 * @since: 2022/07/27 14:47
 */
@Slf4j
@Service
public class NonGridGrabDistributeBServiceImpl implements NonGridGrabDistributeBService {

    @Autowired
    private BaiduMapBService baiduMapBService;
    @Autowired
    private ZmnMQSender zmnMQSender;
    @Autowired
    private RedisTemplate<String, String> stringRedisTemplate;
    @Autowired
    private OrderWorkEsBService orderWorkEsBService;
    @Autowired
    private OrderWorkService orderWorkService;
    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private ZsDistributeWorkBService zsDistributeWorkBService;
    @Autowired
    private NonGridRobOrderConfigBService nonGridRobOrderConfigBService;
    @Autowired
    private NonGridGrabOrderService nonGridGrabOrderService;
    @Autowired
    private NonGridGrabOrderBService nonGridGrabOrderBService;

    @DubboReference(version = com.zmn.base.plat.engine.common.constant.DubboConsts.INTERFACE_VERSION, check = false)
    private PlatDistributeOrderListRemoteService platDistributeOrderListRemoteService;
    @DubboReference(version = com.zmn.gms.dubbo.interfaces.utils.DubboConsts.INTERFACE_VERSION, check = false)
    private GridEngineerListRemoteService gridEngineerListRemoteService;
    @DubboReference(version = com.zmn.performance.dubbo.consts.DubboConsts.INTERFACE_VERSION, check = false)
    private MasterOverviewListRemoteService masterOverviewListRemoteService;
    @DubboReference(version = com.zmn.base.engineer.common.constants.DubboConsts.INTERFACE_VERSION, check = false)
    private EngineerServAreaListRemoteService engineerServAreaListRemoteService;

    // 是否使用导航距离
    protected static final boolean useNavigationDistance = true;

    @Override
    public Boolean autoNonGridDistribute(OrderWorkGrabDTO grabDTO) {
        log.info("autoNonGridDistribute-workId[{}]", grabDTO.getWorkId());
        OrderWork orderWork = grabDTO.getOrderWork();
        if (Objects.isNull(orderWork)) {
            orderWork = orderWorkService.findOrderWorkByKeySrcMaster(grabDTO.getOrderId(), grabDTO.getWorkId());
        }

        // 获取无网格抢单配置
        NonGridRobOrderConfigDRO nonGridGrabConfig = this.getNonGridGrabConfig(orderWork);

        if (Objects.isNull(nonGridGrabConfig) || !Objects.equals(nonGridGrabConfig.getRobOrderStatus(), GlobalConsts.YES)) {
            log.info("订单【{}】不存在无网格派单配置或无网格派单配置未开启", orderWork.getOrderId());
            return Boolean.FALSE;
        }

        if (NumberUtil.isNullOrZero(nonGridGrabConfig.getMaxDistributeDistance()) || nonGridGrabConfig.getMaxDistributeDistance() <= 0) {
            log.info("订单【{}】,最大派单距离配置有误", orderWork.getOrderId());
            return Boolean.FALSE;
        }

        // 默认时间
        String cancelTimeStart = "00:00";
        String cancelTimeEnd = "08:00";
        if (StringUtil.isNotBlank(nonGridGrabConfig.getCancelTimeStart()) && StringUtil.isNotBlank(nonGridGrabConfig.getCancelTimeEnd())) {
            cancelTimeStart = nonGridGrabConfig.getCancelTimeStart();
            cancelTimeEnd = nonGridGrabConfig.getCancelTimeEnd();
        }
        Date receiveTime = orderWork.getReceiveTime();
        int intReceiveTime = receiveTime.getHours() * 60 + receiveTime.getMinutes();
        String[] strStartTime = cancelTimeStart.split(":");
        int intStartTime = Integer.valueOf(strStartTime[0]) * 60 + Integer.valueOf(strStartTime[1]);
        String[] strEndTime = cancelTimeEnd.split(":");
        int intEndTime = Integer.valueOf(strEndTime[0]) * 60 + Integer.valueOf(strEndTime[1]);
        // 过期时间默认是下单时间+过期时长（配置） 但若在非工作时间内从第二天的开始工作时间开始算起
        long expiredTime = receiveTime.getTime() + nonGridGrabConfig.getCancelTimeAfter() * 60 * 1000;

        if (intStartTime > intEndTime) {// 跨天
            if (intReceiveTime > intStartTime || intReceiveTime < intEndTime) {
                LocalDateTime dateTime = LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MIN).plusHours(Integer.valueOf(strEndTime[0])).plusMinutes(Integer.valueOf(strEndTime[1]));
                // 判断下单时间是否在过期开始时间到第二天零点
                if (intReceiveTime > intEndTime) {
                    dateTime = dateTime.plusDays(1);
                }
                expiredTime = dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            }
        } else {
            if (intReceiveTime > intStartTime && intReceiveTime < intEndTime) {
                LocalDateTime dateTime = LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MIN).plusHours(Integer.valueOf(strEndTime[0])).plusMinutes(Integer.valueOf(strEndTime[1]));
                expiredTime = dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            }
        }

        if (expiredTime < System.currentTimeMillis()) {
            log.info("订单【{}】,已过等待时长", orderWork.getOrderId());
            return Boolean.FALSE;
        }

        // 获取最大手头返修单
        DistributeConfigDRO distributeConfig = zsDistributeWorkBService.getDistributeConfig(orderWork);
        // 最大手头返修单：默认0
        int maxReworkCount = 0;
        if (Objects.nonNull(distributeConfig)
                && Objects.nonNull(distributeConfig.getPlatDirectlyDistributeArgsDRO())
                && NumberUtil.isNotNullOrZero(distributeConfig.getPlatDirectlyDistributeArgsDRO().getReworkLimit())
                && distributeConfig.getPlatDirectlyDistributeArgsDRO().getReworkLimit() > 0) {
            maxReworkCount = distributeConfig.getPlatDirectlyDistributeArgsDRO().getReworkLimit();
        }

        OrderDetail orderDetail = orderDetailService.findOrderDetailByKeySrcMaster(orderWork.getOrderId());
        DistributeMasterQuery distributeMasterQuery = zsDistributeWorkBService.buildDistributeMasterQuery(orderWork, orderDetail, true);

        // 满足最大派单距离工程师id集合
        Integer maxDistributeDistance = Optional.ofNullable(nonGridGrabConfig.getMaxDistributeDistance()).orElse(0) * 1000;
        List<Integer> gridMasterIdList = this.getGridMasterIdList(orderWork.getCityId(), orderDetail.getLongitude(), orderDetail.getLatitude(), 0, maxDistributeDistance);

        if (CollectionUtil.isNullOrEmpty(gridMasterIdList)) {
            log.info("订单【{}】,最大派单距离内无工程师", orderWork.getOrderId());
            return Boolean.FALSE;
        }
        distributeMasterQuery.setMatchMasterIdList(gridMasterIdList);
        // 查询工程师列表--强制派单--满足技能
        // 后台技能
        List<List<DistributeOrderSkillQuery>> servSkillLists = zsDistributeWorkBService.buildSkillQueryLists(distributeMasterQuery.getProductId(),
                distributeMasterQuery.getShowProductId(),
                distributeMasterQuery.getServCategId(), distributeMasterQuery.getCategId());
        // 可抢列表
        List<ZsOrderDistributeVO> masterList = this.listMandatoryDistributeMasterByQuery(distributeMasterQuery, servSkillLists);

        // 查询工程师列表--强制派单--不满足技能
        if (CollectionUtil.isNotNullOrEmpty(masterList)) {
            List<Integer> excludeMasterIdList = masterList.stream().map(ZsOrderDistributeVO::getMasterId).collect(Collectors.toList());
            distributeMasterQuery.setExcludeMasterIdList(excludeMasterIdList);
        }
        // 不可抢列表
        List<ZsOrderDistributeVO> noSkillMasterList = this.listMandatoryDistributeMasterByQuery(distributeMasterQuery, null);
        if (CollectionUtil.isNotNullOrEmpty(noSkillMasterList)) {
            noSkillMasterList.forEach(vo -> vo.setFailReasonCode(OrderDistributeConsts.UN_GRID_GRAB_SKILL_CODE));
        } else {
            noSkillMasterList = Lists.newArrayList();
        }

        // 查询工单信息（手头单（不含待件&拉修），手头返修单（不含待件&拉修），当月已派他人返修单） -- 技能不满足的不需要去查询了
        this.getMasterListWorkInfo(masterList, distributeMasterQuery);

        // 基础过滤--目前只考虑-子公司-直营-最大手头返修单
        this.baseDistributeFilter(nonGridGrabConfig, masterList, maxReworkCount);
        this.baseDistributeFilter(nonGridGrabConfig, noSkillMasterList, maxReworkCount);
        // 先查询工程师位置 如果没有计算手头单中心点
        // 调用工程师服务查询app位置在线和服务范围状态
//            this.getMasterOnlineStatusAndScopeStatus(masterList, distributeMasterQuery);

        // 根据工程师手头单计算中心点--如果未获取到工程师经纬度的会去计算中心点并将中心点当做工程师的位置
        this.calcMasterListOrderCenter(masterList, distributeMasterQuery);

        // 计算距离之前需要将没有位置的工程师过滤掉
        masterList = masterList.stream().filter(vo -> (Objects.nonNull(vo.getMasterLng()) && Objects.nonNull(vo.getMasterLat()))).collect(Collectors.toList());

        // 计算距离
        this.calcMasterOrderDistance(distributeMasterQuery.getLongitude(), distributeMasterQuery.getLatitude(), masterList);

        // 距离过滤
        masterList = this.distanceFilter(nonGridGrabConfig, masterList, noSkillMasterList);

        List<NonGridGrabOrder> list = Lists.newArrayList();

        // 保存无网格抢单工程师
        OrderWork finalOrderWork = orderWork;
        if (CollectionUtil.isNotNullOrEmpty(masterList)) {
            list.addAll(masterList.stream().map(vo -> buildNonGridGrabOrder(finalOrderWork, orderDetail, vo)).collect(Collectors.toList()));
        }

        if (CollectionUtil.isNotNullOrEmpty(noSkillMasterList)) {
            list.addAll(noSkillMasterList.stream().map(vo -> buildNonGridGrabOrder(finalOrderWork, orderDetail, vo)).collect(Collectors.toList()));
        }

        if (CollectionUtil.isNullOrEmpty(list)) {
            log.info("【{}】无网格抢单不存在如何满足条件的工程师", orderWork.getOrderId());
            return Boolean.FALSE;
        }

        nonGridGrabOrderService.batchInsert(list);

        // 修改工单派单模式
        nonGridGrabOrderBService.modifyGrabType(grabDTO);

        // 保存无网格抢单存根
        String redisKey = String.format(RedisKeyConsts.GRAB_CONSUME_NON_GRID_ORDER_KEY, orderWork.getWorkId());
        // 过期时间=下单时间+过期时长 （同时需判断工作时间-配置）
        long curTime = System.currentTimeMillis();
        stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(curTime), expiredTime - curTime, TimeUnit.MILLISECONDS);

        // 派单队列数据
        MqGrabOrderDTO mqGrabOrderDTO = new MqGrabOrderDTO();
        mqGrabOrderDTO.setWorkId(orderWork.getWorkId());
        mqGrabOrderDTO.setDateTime(curTime);
        // 发送至定时队列
        String keyMQ = "oms_non_grid_grab_" + mqGrabOrderDTO.getWorkId();
        zmnMQSender.sendWithStartDeliverTime(MqTopicConsts.ZMN_TOPIC_OMS, OmsMqTagConsts.GRAB_NON_GRID_ORDER_END_TIME_MQ_TAG, keyMQ, JSON.toJSONString(mqGrabOrderDTO), expiredTime);

        return Boolean.TRUE;
    }

    /**
     * 构建无网格抢单对象
     *
     * @param orderWork
     * @param orderDetail
     * @param vo
     * @return
     */
    private NonGridGrabOrder buildNonGridGrabOrder(OrderWork orderWork, OrderDetail orderDetail, ZsOrderDistributeVO vo) {
        NonGridGrabOrder nonGridGrabOrder = new NonGridGrabOrder();
        nonGridGrabOrder.setOrderId(orderWork.getOrderId());
        nonGridGrabOrder.setWorkId(orderWork.getOrderId());
        nonGridGrabOrder.setMasterId(vo.getMasterId());
        if (NumberUtil.isNotNullOrZero(vo.getFailReasonCode())) {
            nonGridGrabOrder.setUnGrabReasonCode(vo.getFailReasonCode());
            nonGridGrabOrder.setGrabStatus(GlobalConsts.NO);
        } else {
            nonGridGrabOrder.setUnGrabReasonCode(GlobalConsts.NONE);
            nonGridGrabOrder.setGrabStatus(GlobalConsts.YES);
        }
        nonGridGrabOrder.setProductInfo(orderDetail.getProductInfo());
        nonGridGrabOrder.setDutyTime(orderWork.getDutyTime());
        if (Objects.nonNull(vo.getDistance())) {
            nonGridGrabOrder.setDistance(vo.getDistance().intValue());
        } else {
            nonGridGrabOrder.setDistance(0);
        }
        nonGridGrabOrder.setLongitude(orderDetail.getLongitude());
        nonGridGrabOrder.setLatitude(orderDetail.getLatitude());
        nonGridGrabOrder.setValidStatus(GlobalConsts.YES);
        nonGridGrabOrder.setCreater(GlobalConsts.getOperatorTypeName(GlobalConsts.OPERATE_USER_TYPE_SYSTEM));
        nonGridGrabOrder.setUpdater(GlobalConsts.getOperatorTypeName(GlobalConsts.OPERATE_USER_TYPE_SYSTEM));
        return nonGridGrabOrder;
    }

    /**
     * 过滤距离不满足的情况
     *
     * @param nonGridGrabConfig
     * @param masterList
     * @param noSkillMasterList
     */
    private List<ZsOrderDistributeVO> distanceFilter(NonGridRobOrderConfigDRO nonGridGrabConfig, List<ZsOrderDistributeVO> masterList, List<ZsOrderDistributeVO> noSkillMasterList) {
        if (CollectionUtil.isNullOrEmpty(masterList)) {
            return null;
        }
        int maxRobDistance = Optional.ofNullable(nonGridGrabConfig.getMaxRobDistance()).orElse(0) * 1000; // 最大抢单距离 米
//        int maxDistributeDistance = Optional.ofNullable(nonGridGrabConfig.getMaxDistributeDistance()).orElse(0) * 1000; // 最大派单距离 米

        // 过滤没有距离和最大派单距离不满足的情况
//        masterList = masterList.stream().filter(vo -> Objects.nonNull(vo.getDistance())).filter(vo -> vo.getDistance() < maxDistributeDistance).collect(Collectors.toList());
        masterList = masterList.stream().filter(vo -> Objects.nonNull(vo.getDistance())).collect(Collectors.toList());
        if (maxRobDistance <= 0) {
            return masterList;
        }

        List<ZsOrderDistributeVO> reserveList = Lists.newArrayListWithCapacity(masterList.size());
        if (CollectionUtil.isNotNullOrEmpty(masterList)) {
            for (ZsOrderDistributeVO vo : masterList) {
                if (vo.getDistance() > maxRobDistance) {
                    if (NumberUtil.isNullOrZero(vo.getFailReasonCode())) {
                        vo.setFailReasonCode(OrderDistributeConsts.UN_GRID_GRAB_MAX_DISTANCE_CODE);
                    }
                    noSkillMasterList.add(vo);
                } else {
                    reserveList.add(vo);
                }
            }
        }
//        masterList.removeAll(noSkillMasterList);
        return reserveList;
    }

    /**
     * 基础过滤
     *
     * @param nonGridGrabConfig
     * @param masterList
     * @param maxReworkCount
     */
    private void baseDistributeFilter(NonGridRobOrderConfigDRO nonGridGrabConfig,
                                      List<ZsOrderDistributeVO> masterList,
                                      int maxReworkCount) {
        if (CollectionUtil.isNullOrEmpty(masterList)) {
            return;
        }

        // 子公司-直营 无网格抢单配置 直接丢弃
        if (NumberUtil.isNotNullOrZero(nonGridGrabConfig.getEngineerType())) {
            masterList = masterList.stream().filter(vo -> Objects.equals(vo.getAgent(), nonGridGrabConfig.getEngineerType())).collect(Collectors.toList());
        }

        // 过滤家修匠工程师
        if (BooleanUtil.isFalse(nonGridGrabConfig.getEngineerCompanyTypeOfHomeRepairman())) {
            masterList = masterList.stream().filter(vo -> !Objects.equals(vo.getCompanyId(), CompanyConsts.ZMN_JXJ_COMPANY_ID)).collect(Collectors.toList());
        }

        // 过滤子公司工程师
        if (BooleanUtil.isFalse(nonGridGrabConfig.getEngineerCompanyTypeOfSubsidiary())) {
            masterList = masterList.stream().filter(vo -> Objects.equals(vo.getCompanyId(), CompanyConsts.ZMN_JXJ_COMPANY_ID)).collect(Collectors.toList());
        }

        // 最大手头返修单
        masterList.forEach(vo -> {
            if (maxReworkCount > 0 && vo.getHandReworkCount() >= maxReworkCount && NumberUtil.isNullOrZero(vo.getFailReasonCode())) {
                vo.setFailReasonCode(OrderDistributeConsts.UN_GRID_GRAB_HAND_REWORK_CODE);
            }
        });

    }

    /**
     * 获取无网格抢单配置
     *
     * @param orderWork
     * @return
     */
    private NonGridRobOrderConfigDRO getNonGridGrabConfig(OrderWork orderWork) {
        NonGridRobOrderConfigQuery configQuery = NonGridRobOrderConfigQuery.builder()
                .plat(orderWork.getPlatWork())
                .cityId(orderWork.getCityId())
                .bizType(orderWork.getBizType())
                .build();
        List<NonGridRobOrderConfigDRO> configDROS = nonGridRobOrderConfigBService.listNoGridRobConfigByQuery(configQuery);
        log.info("获取无网格抢单配置：{}", JSON.toJSONString(configDROS));
        if (CollectionUtil.isNotNullOrEmpty(configDROS)) {
            return configDROS.get(0);
        }
        return null;
    }

    /**
     * 查询派单总览表
     *
     * @param masterList
     * @param distributeMasterQuery
     * @return
     */
    private List<ZsOrderDistributeVO> getMasterListWorkInfo(List<ZsOrderDistributeVO> masterList, DistributeMasterQuery distributeMasterQuery) {
        if (CollectionUtil.isNullOrEmpty(masterList)) {
            return null;
        }

        // 转化为map
        Map<Integer, ZsOrderDistributeVO> masterStatMap = masterList.stream().collect(Collectors.toMap(ZsOrderDistributeVO::getMasterId, e -> e, (k1, k2) -> k1));

        Date queryDate;
        String fieldName;

        // 没有预约时间就用派单时间查询
        if (distributeMasterQuery.getDutyTime() == null) {
            queryDate = DateUtil.getNow();
            fieldName = "distributeTime";
        } else {
            queryDate = distributeMasterQuery.getDutyTime();
            fieldName = "dutyTime";
        }

        // 调用绩效派单总览表接口
        MasterOverviewDIO masterOverviewDIO = new MasterOverviewDIO();
        masterOverviewDIO.setCityId(distributeMasterQuery.getCityId());
        masterOverviewDIO.setExcludeOrderId(distributeMasterQuery.getOrderId());
        masterOverviewDIO.setDatumDate(queryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
        masterOverviewDIO.setField(fieldName);
        masterOverviewDIO.setMasterIdList(masterStatMap.keySet().stream().collect(Collectors.toList()));

        log.info("masterOverviewListRemoteService#listIntegrateByDio:{}", JSON.toJSONString(masterOverviewDIO));
        ResponseDTO<List<MasterOverviewIntegrateDRO>> responseDTO = masterOverviewListRemoteService.listIntegrateByDio(masterOverviewDIO);
        log.info("masterOverviewListRemoteService#listIntegrateByDio[responseDTO]:{}", JSON.toJSONString(responseDTO));

        if (!responseDTO.isSuccess() || CollectionUtils.isEmpty(responseDTO.getData())) {
            return masterList;
        }

        for (MasterOverviewIntegrateDRO dro : responseDTO.getData()) {

            ZsOrderDistributeVO vo = masterStatMap.get(dro.getMasterId());

            if (vo != null) {
                vo.setHandCount(dro.getDoingCount() == null ? 0 : dro.getDoingCount());
                vo.setHandReworkCount(dro.getDoingReworkCount() == null ? 0 : dro.getDoingReworkCount());
                vo.setMonthReworkCount(dro.getCurrentMonthDistributeOtherReworkCount() == null ? 0 : dro.getCurrentMonthDistributeOtherReworkCount());
                vo.setTodaySuccessCount(dro.getCompleteCount() == null ? 0 : dro.getCompleteCount());
                vo.setDistributeNewCount(dro.getDistributeNewCount() == null ? 0 : dro.getDistributeNewCount());
                vo.setYesterdayDistributeNewCount(dro.getYesterdayDistributeNewCount() == null ? 0 : dro.getYesterdayDistributeNewCount());
                vo.setDatumDistributeCount(dro.getDatumDistributeCount() == null ? 0 : dro.getDatumDistributeCount());
                vo.setDatumDoingCount(dro.getDatumDoingCount() == null ? 0 : dro.getDatumDoingCount());
                vo.setDatumDistributeDutyCount(dro.getDatumDistributeDutyCount() == null ? 0 : dro.getDatumDistributeDutyCount());
                vo.setDatumPastDistributeCount(dro.getDatumPastDistributeCount() == null ? 0 : dro.getDatumPastDistributeCount());
                vo.setDatumOrderHourRatio(dro.getDatumOrderHourRatio() == null ? 0 : dro.getDatumOrderHourRatio());
                vo.setDeliveryAndWaitPartCount(dro.getDeliveryAndWaitPartCount() == null ? 0 : dro.getDeliveryAndWaitPartCount());
                vo.setOutGridCount(dro.getOutGridCount() == null ? 0 : dro.getOutGridCount());
                vo.setSysAcrossGridCount(dro.getSysAcrossGridCount() == null ? 0 : dro.getSysAcrossGridCount());
                vo.setYesterdayTiltDistributeCount(dro.getYesterdayTiltDistributeCount() == null ? 0 : dro.getYesterdayTiltDistributeCount());
                vo.setRedistributeCount(dro.getRedistributeCount() == null ? 0 : dro.getRedistributeCount());
                vo.setFalseRedistributeScore(dro.getFalseRedistributeScore() == null ? 0 : dro.getFalseRedistributeScore());
                vo.setFalseCancelScore(dro.getFalseCancelScore() == null ? 0 : dro.getFalseCancelScore());
                vo.setReworkReminderCount(dro.getReworkReminderCount() == null ? 0 : dro.getReworkReminderCount());
            }

        }

        return masterList;
    }


    /**
     * 调用GMS获取对应网格内工程师id列表
     */
    private List<Integer> getGridMasterIdList(Integer cityId, Double lng, Double lat, Integer minDistance, Integer maxDistance) {

        // 生成调用参数
        DistanceGridEngInfoQuery distanceGridEngInfoQuery = new DistanceGridEngInfoQuery();
        distanceGridEngInfoQuery.setCityId(cityId);
        distanceGridEngInfoQuery.setLng(lng);
        distanceGridEngInfoQuery.setLat(lat);
        distanceGridEngInfoQuery.setMinDistance(minDistance);
        distanceGridEngInfoQuery.setMaxDistance(maxDistance);

        // 调用工程师网格信息接口
        log.info("listGridEngineerByDistance：{}", JSON.toJSONString(distanceGridEngInfoQuery));
        ResponseDTO<List<Integer>> responseDTO = gridEngineerListRemoteService.listGridEngineerByDistance(distanceGridEngInfoQuery);
        log.info("listGridEngineerByDistance[responseDTO]：{}", responseDTO);

        if (!responseDTO.isSuccess() || responseDTO.getData() == null) {
            return null;
        }

        return responseDTO.getData();
    }

    /**
     * 查询工程师列表-强制派单
     *
     * @param distributeMasterQuery
     * @return
     */
    private List<ZsOrderDistributeVO> listMandatoryDistributeMasterByQuery(DistributeMasterQuery distributeMasterQuery, List<List<DistributeOrderSkillQuery>> servSkillLists) {
        // 生成调用参数
        EngineerEnforceDistributeOrderQuery platDirectlySendQuery = new EngineerEnforceDistributeOrderQuery();
        platDirectlySendQuery.setSerialNumber(UUID.randomUUID().toString());
        platDirectlySendQuery.setPlat(distributeMasterQuery.getPlat());
        platDirectlySendQuery.setBizType(distributeMasterQuery.getBizType());
        platDirectlySendQuery.setChannelId(distributeMasterQuery.getChannelId());
        platDirectlySendQuery.setCityId(distributeMasterQuery.getCityId());
        platDirectlySendQuery.setOrderLat(distributeMasterQuery.getLatitude());
        platDirectlySendQuery.setOrderLng(distributeMasterQuery.getLongitude());
        platDirectlySendQuery.setDutyTime(distributeMasterQuery.getDutyTime());
        platDirectlySendQuery.setServCategId(distributeMasterQuery.getServCategId());
        platDirectlySendQuery.setCategId(distributeMasterQuery.getShowCategId());
        platDirectlySendQuery.setProductGroupId(distributeMasterQuery.getServProductGroupId());
        platDirectlySendQuery.setMatchEngineerList(distributeMasterQuery.getMatchMasterIdList());
        platDirectlySendQuery.setExcludeEngineerList(distributeMasterQuery.getExcludeMasterIdList());
        platDirectlySendQuery.setServSkillList(servSkillLists);
        platDirectlySendQuery.setMaxProbationCycle(7);

        // 调用查询派单工程师接口
        log.info("listMandatoryDistributeMasterByQuery#listForEnforceDistributeOrder 入参：{}", JSON.toJSONString(platDirectlySendQuery));
        ResponseDTO<List<EngineerDistributeOrderDRO>> responseDTO = platDistributeOrderListRemoteService.listForEnforceDistributeOrder(platDirectlySendQuery);
        log.info("listMandatoryDistributeMasterByQuery#listForEnforceDistributeOrder  出参 [responseDTO]：{}", JSON.toJSONString(responseDTO));

        if (!responseDTO.isSuccess() || responseDTO.getData() == null) {
            return Lists.newArrayListWithCapacity(0);
        }

        List<EngineerDistributeOrderDRO> distributeOrderDROList = responseDTO.getData();
        if (CollectionUtils.isEmpty(distributeOrderDROList)) {
            return Lists.newArrayListWithCapacity(0);
        }

        List<ZsOrderDistributeVO> distributeMasterList = Lists.newArrayListWithCapacity(distributeOrderDROList.size());
        distributeOrderDROList.forEach((item) -> {
            ZsOrderDistributeVO vo = this.transformDistributeObjectDRO2VO(item);
            distributeMasterList.add(vo);
        });

        return distributeMasterList;
    }


    private ZsOrderDistributeVO transformDistributeObjectDRO2VO(EngineerDistributeOrderDRO dro) {
        ZsOrderDistributeVO vo = new ZsOrderDistributeVO();

        vo.setMasterId(dro.getEngineerId());
        vo.setMasterPhone(dro.getEngineerPhone());
        vo.setMasterName(dro.getEngineerName());

        if (Objects.nonNull(dro.getLatitude()) && Objects.nonNull(dro.getLongitude()) && dro.getLatitude() > 0.0D && dro.getLongitude() > 0.0D) {
            vo.setMasterLat(dro.getLatitude());
            vo.setMasterLng(dro.getLongitude());
        }

        vo.setMaxHandCount(dro.getMaxReceiveNum() == null ? 0 : dro.getMaxReceiveNum());
        vo.setMaxDayHandNumber(dro.getMaxDayReceiveNum() == null ? 0 : dro.getMaxDayReceiveNum());
        vo.setDailyNewOrders(dro.getDayDistributeNum() == null ? 0 : dro.getDayDistributeNum());
        vo.setMaxRasshuPpartsNumber(dro.getMaxRasshuPartsNum() == null ? 0 : dro.getMaxRasshuPartsNum());
        vo.setSkillServCategoryId(dro.getMainServCategId());
        vo.setSkillServCategoryName(dro.getMainServCategName());
        vo.setSkillCategoryId(dro.getMainCategId());
        vo.setSkillCategoryName(dro.getMainCategName());
        vo.setAvatarSrc(dro.getAvatarSrc());
        vo.setMainSkillId(vo.getSkillServCategoryId() + "-" + vo.getSkillCategoryId());
        vo.setMainSkillName(vo.getSkillServCategoryName() + "-" + vo.getSkillCategoryName());
        vo.setEngineerStatus(Optional.ofNullable(dro.getStopOrderStatus()).orElse(EngineerStopOrderStatusEnum.NORMAL.getCode()));
        vo.setEngineerStatusName(EngineerStopOrderStatusEnum.getEnumByCode(dro.getStopOrderStatus()).getName());
        vo.setAgent(dro.getCooperationType());
        vo.setSpId(dro.getSpCompanyId());
        vo.setSpName(dro.getSpCompanyName());
        vo.setCompanyId(dro.getSubCompanyId());
        vo.setNovice(Objects.equals(dro.getNoviceLabelStatus(), 2) ? Boolean.TRUE : Boolean.FALSE);
        vo.setStarLevelScore(Optional.ofNullable(dro.getStarLevelScore()).orElse(0.0D));
        vo.setChannelExclusive(dro.getChannelExclusive());
        vo.setStarLevel(dro.getStarLevel());
        vo.setProductGroupId(dro.getProductGroupId());
        vo.setExcellentFlag(dro.getExcellentFlag());

        return vo;
    }

    // 获取工程师在线及服务范围状态信息
    private void getMasterOnlineStatusAndScopeStatus(List<ZsOrderDistributeVO> masterList, DistributeMasterQuery distributeMasterQuery) {
        if (CollectionUtil.isNullOrEmpty(masterList)) {
            return;
        }

        // 工程师id列表
        List<Integer> masterIds = masterList.stream().map(ZsOrderDistributeVO::getMasterId).collect(Collectors.toList());

        // 调用获取工程师在线及服务范围状态信息dubbo
        EngineerServScopeStatusQuery engineerOnlineStatusAndScopeStatusDIO = new EngineerServScopeStatusQuery();

        Set<Integer> integerSet = new HashSet<>(masterIds);
        engineerOnlineStatusAndScopeStatusDIO.setEngineerIdSet(integerSet);
        engineerOnlineStatusAndScopeStatusDIO.setCityId(distributeMasterQuery.getCityId());
        engineerOnlineStatusAndScopeStatusDIO.setCountyId(distributeMasterQuery.getCountyId());
        if (Objects.equals(distributeMasterQuery.getStreetSource(), OrderConsts.STREET_SOURCE_NONE) ||
                Objects.equals(distributeMasterQuery.getStreetSource(), OrderConsts.STREET_SOURCE_DEFAULT)) {
            engineerOnlineStatusAndScopeStatusDIO.setStreetId(distributeMasterQuery.getStreetId());
        } else {
            engineerOnlineStatusAndScopeStatusDIO.setStreetId(0);
        }
        engineerOnlineStatusAndScopeStatusDIO.setLon(distributeMasterQuery.getLongitude());
        engineerOnlineStatusAndScopeStatusDIO.setLat(distributeMasterQuery.getLatitude());

        log.info("getMasterOnlineStatusAndScopeStatus#listEngineerServAreaScopeStatusByQuery 入参 [{}]", JSON.toJSONString(engineerOnlineStatusAndScopeStatusDIO));
        ResponseDTO<List<EngineerServScopeStatusDRO>> responseDTO = engineerServAreaListRemoteService.listEngineerServAreaScopeStatusByQuery(engineerOnlineStatusAndScopeStatusDIO);
        log.info("getMasterOnlineStatusAndScopeStatus#listEngineerServAreaScopeStatusByQuery 出参 [{}]", JSON.toJSONString(responseDTO));

        if (!responseDTO.isSuccess()) {
            return;
        }

        List<EngineerServScopeStatusDRO> statusList = responseDTO.getData();
        if (CollectionUtils.isEmpty(statusList)) {
            return;
        }

        // 设置状态到每一个vo对象
        Map<Integer, ZsOrderDistributeVO> masterMap = masterList.stream().collect(Collectors.toMap(ZsOrderDistributeVO::getMasterId, e -> e, (k1, k2) -> k1));

        statusList.forEach(item -> {
            ZsOrderDistributeVO vo = masterMap.get(item.getEngineerId());
            if (vo != null) {
                vo.setOnlineStatus(item.getOnlineStatus());
                vo.setScopeStatus(item.getScopeStatus());

                if (!Objects.equals(com.zmn.common.constant.GlobalConsts.YES, vo.getOnlineStatus())) {
                    vo.setMasterLng(null);
                    vo.setMasterLat(null);
                }
            }
        });
    }

    /**
     * 根据工程师手头单计算中心点 -- 只计算工程师位置为空的的中心点 --特殊处理 将订单中心点位置当作工程师的经纬度
     *
     * @param masterList
     * @param distributeMasterQuery
     * @return
     */
    private void calcMasterListOrderCenter(List<ZsOrderDistributeVO> masterList, DistributeMasterQuery distributeMasterQuery) {

        // 判断预约时间
        if (Objects.isNull(distributeMasterQuery.getDutyTime())) {
            log.info("[{}]预约时间为空，不计算手头单中心点", distributeMasterQuery.getOrderId());
            return;
        }

        // 判断师傅列表
        if (CollectionUtil.isNullOrEmpty(masterList)) {
            log.info("[{}]工程师为空，不计算手头单中心点", distributeMasterQuery.getOrderId());
            return;
        }

        // 查询工程师手头单
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 状态结果：已派单-已领单-已上门 / 进行中
        boolQueryBuilder.must(QueryBuilders.termsQuery("status",
                Lists.newArrayList(OrderStatusConsts.WORK_STATUS_DISTRIBUTE, OrderStatusConsts.WORK_STATUS_TAKE, OrderStatusConsts.WORK_STATUS_VISIT)));
        boolQueryBuilder.must(QueryBuilders.termQuery("resultStatus", OrderStatusConsts.WORK_RESULT_DOING));

        // 新单，返修单
        boolQueryBuilder.must(QueryBuilders.termsQuery("type", Lists.newArrayList(OrderConsts.ORDER_TYPE_NEW, OrderConsts.ORDER_TYPE_REWORK)));

        // 排除测试单
        boolQueryBuilder.must(QueryBuilders.termQuery("isTest", false));
        // 排除重复单
        boolQueryBuilder.must(QueryBuilders.termQuery("isDuplicate", false));
        // 排除剔除工程师绩效的工单标签
        boolQueryBuilder.mustNot(QueryBuilders.termsQuery("orderTagList.categoryIdList", Lists.newArrayList(OrderTagConsts.TAG_CATEGORY_ID_EXCLUDE_PERFORMANCE)));
        // 1028 排除突破组订单
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("servProductGroupId", 1028));
        // 排除拉修
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("hasDelivery", true));
        // 排除待件
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("hasWaitPart", true));

        // 预约时间同一天
        String dutyStartTimeStr = DateUtil.toString(DateUtil.getDateStart(distributeMasterQuery.getDutyTime()));
        String dutyEndTimeStr = DateUtil.toString(DateUtil.getDateEnd(distributeMasterQuery.getDutyTime()));
        boolQueryBuilder.must(QueryBuilders.rangeQuery("dutyTime").gte(dutyStartTimeStr).lte(dutyEndTimeStr));

        // 包含工程师条件 -- 没有工程师位置经纬度
        Set<Integer> masterIdList = masterList.stream().filter(vo -> Objects.isNull(vo.getMasterLat()) || Objects.isNull(vo.getMasterLng())).map(ZsOrderDistributeVO::getMasterId).collect(Collectors.toSet());
        boolQueryBuilder.must(QueryBuilders.termsQuery("masterId", masterIdList));
        log.debug("[{}]中心点查询语句：【{}】-【{}】", distributeMasterQuery.getOrderId(), boolQueryBuilder.toString(), JSON.toJSONString(boolQueryBuilder));

        NativeSearchQuery searchQuery = new NativeSearchQuery(boolQueryBuilder);
        searchQuery.setPageable(PageRequest.of(0, 500));
        String[] columns = {"id", "masterId", "contact.location"};
        SourceFilter sourceFilter = new FetchSourceFilter(columns, null);
        searchQuery.addSourceFilter(sourceFilter);

        List<EsOrderWork> esOrderWorks = orderWorkEsBService.listPageByQuery(searchQuery);
        List<Long> resultList = esOrderWorks.stream().map(EsOrderWork::getId).collect(Collectors.toList());
        log.debug("[{}]查询结果：【{}】", JSON.toJSONString(resultList));
        // 预约时间没有手头单返回
        if (CollectionUtil.isNullOrEmpty(esOrderWorks)) {
            log.info("[{}]工程师手头单为空", distributeMasterQuery.getOrderId());
            return;
        }

        // 根据工程师手头单计算中心点，保存到VO订单中心位置
        Map<Integer, List<EsOrderWork>> masterMap = esOrderWorks.stream().collect(Collectors.groupingBy(EsOrderWork::getMasterId));
        masterList.forEach(vo -> {
            List<EsOrderWork> list = masterMap.get(vo.getMasterId());
            log.debug("[{}]工程师id[{}],工单数据:[{}]", distributeMasterQuery.getOrderId(), vo.getMasterId(), JSON.toJSONString(list));
            // 没有手头单返回，中心点为空
            if (CollectionUtil.isNullOrEmpty(list)) {
                return;
            }

            // 当手头单为1单时，手头单位置就是中心点
            if (list.size() == 1) {
                String[] latLngArray = list.get(0).getContact().getLocation().split(",");
                vo.setMasterLat(Double.valueOf(latLngArray[0]));
                vo.setMasterLng(Double.valueOf(latLngArray[1]));
                return;
            }

            // 当手头单大于1单时，计算中心点
            BigDecimal lat = new BigDecimal(0);
            BigDecimal lng = new BigDecimal(0);
            for (EsOrderWork esOrderWork : list) {
                String[] latLngArray = esOrderWork.getContact().getLocation().split(",");
                lat = lat.add(new BigDecimal(latLngArray[0]));
                lng = lng.add(new BigDecimal(latLngArray[1]));
            }

            Double latitude = lat.divide(new BigDecimal(list.size()), 6, RoundingMode.HALF_UP).doubleValue();
            Double longitude = lng.divide(new BigDecimal(list.size()), 6, RoundingMode.HALF_UP).doubleValue();

            vo.setMasterLat(latitude);
            vo.setMasterLng(longitude);
        });
        log.debug("[{}]工程师手头单中心点出参：【{}】", distributeMasterQuery.getOrderId(), JSON.toJSONString(masterList));
    }

    /**
     * 计算距离（米） （距离：工程师当前位置，和当前订单的位置距离）
     */
    private void calcMasterOrderDistance(Double longitude, Double latitude, List<ZsOrderDistributeVO> masterList) {
        if (longitude == null || latitude == null || CollectionUtils.isEmpty(masterList)) {
            return;
        }

        if (useNavigationDistance) {
            boolean ret = this.calcMasterOrderNavigationDistance(longitude, latitude, masterList);

            // 调用百度接口失败，使用简单方法计算
            if (!ret) {
                this.calcMasterOrderSimpleDistance(longitude, latitude, masterList);
            } else {
                // 把距离为0的按简单距离再处理一遍
                for (ZsOrderDistributeVO vo : masterList) {
                    if (vo.getDistance() != null && vo.getDistance() == 0.0D) {
                        double dDistance = getCornerDistance(longitude, latitude, vo.getMasterLng(), vo.getMasterLat());
                        vo.setDistance(dDistance);
                    }
                }
            }
        } else {
            this.calcMasterOrderSimpleDistance(longitude, latitude, masterList);
        }
    }

    /**
     * 计算导航距离 （距离：工程师当前位置，和当前订单的位置距离）
     */
    private boolean calcMasterOrderNavigationDistance(Double longitude, Double latitude, List<ZsOrderDistributeVO> masterList) {
        if (longitude == null || latitude == null || CollectionUtils.isEmpty(masterList)) {
            return true;
        }

        // 订单位置
        Point orderPoint = new Point(longitude, latitude);

        // 工程师位置，有位置信息的工程师才运算
        List<ZsOrderDistributeVO> descMasterList = Lists.newArrayListWithCapacity(masterList.size());
        List<Point> descPointList = Lists.newArrayListWithCapacity(masterList.size());

        for (ZsOrderDistributeVO vo : masterList) {
            Double masterLat = vo.getMasterLat();
            Double masterLng = vo.getMasterLng();

            if (masterLat != null && masterLng != null) {
                descMasterList.add(vo);
                descPointList.add(new Point(masterLng, masterLat));
            }
        }

        // 没有有位置信息的工程师不处理
        if (CollectionUtils.isEmpty(descMasterList)) {
            return true;
        }

        List<Integer> multiNavigationDistance = baiduMapBService.getMultiRidingDistance(orderPoint, descPointList);

        // 调用百度接口失败，返回失败
        if (multiNavigationDistance == null) {
            return false;
        }

        for (int i = 0; i < multiNavigationDistance.size(); i++) {
            Integer mDistance = multiNavigationDistance.get(i);
            descMasterList.get(i).setDistance(new Double(mDistance));
        }

        return true;
    }

    /**
     * 计算直线距离（米） （距离：工程师当前位置，和当前订单的位置距离）
     */
    private void calcMasterOrderSimpleDistance(Double longitude, Double latitude, List<ZsOrderDistributeVO> masterList) {
        if (longitude == null || latitude == null || CollectionUtils.isEmpty(masterList)) {
            return;
        }

        // 工程师位置，有位置信息的工程师才运算
        List<ZsOrderDistributeVO> descMasterList = Lists.newArrayListWithCapacity(masterList.size());

        for (ZsOrderDistributeVO vo : masterList) {
            Double masterLat = vo.getMasterLat();
            Double masterLng = vo.getMasterLng();

            if (masterLat != null && masterLng != null && masterLat > 0 && masterLng > 0) {
                descMasterList.add(vo);
            }
        }

        // 没有有位置信息的工程师不处理
        if (CollectionUtils.isEmpty(descMasterList)) {
            return;
        }

        List<Integer> multiSimpleDistance = Lists.newArrayListWithCapacity(descMasterList.size());
        for (ZsOrderDistributeVO vo : descMasterList) {
            double dDistance = getCornerDistance(longitude, latitude, vo.getMasterLng(), vo.getMasterLat());
            multiSimpleDistance.add((int) dDistance);
        }

        for (int i = 0; i < multiSimpleDistance.size(); i++) {
            Integer mDistance = multiSimpleDistance.get(i);
            descMasterList.get(i).setDistance(new Double(mDistance));
        }
    }

    /**
     * 根据两点间经纬度坐标（double值），计算两点间拐角距离，单位为米
     *
     * @param lng1
     * @param lat1
     * @param lng2
     * @param lat2
     * @return
     */
    private static double getCornerDistance(double lng1, double lat1, double lng2, double lat2) {
        double s = 0.0;

        do {
            // 位置相同
            if (lng1 == lng2 && lat1 == lat2) {
                break;
            }

            // 经度或者纬度之一相同
            if (lng1 == lng2 || lat1 == lat2) {
                s = MathUtil.GetDistance(lng1, lat1, lng2, lat2);
                break;
            }

            // 经度和纬度都不同，转换为两个拐弯距离之和
            double s1 = MathUtil.GetDistance(lng1, lat1, lng1, lat2);
            double s2 = MathUtil.GetDistance(lng1, lat1, lng2, lat1);

            s = s1 + s2;
        } while (false);
        return s;
    }
}
